﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Compiler.PrebuildParser
{
    public static class MathParser
    {
        public static Parser Build()
        {
            Terminal _id = new Terminal(0, "ID");
            Terminal _add = new Terminal(1, "+");
            Terminal _mul = new Terminal(2, "*");
            Terminal _lb = new Terminal(3, "lb");
            Terminal _rb = new Terminal(4, "rb");
            Terminal _num = new Terminal(5, "NUM");

            Token id = new Token(@"@?[a-zA-Z_][a-zA-Z_0-9]*", "IDENTIFIER", Int32.MaxValue, _id);
            Token add = new Token(@"\+", "ADD", 1, _add);
            Token mul = new Token(@"\*", "MUL", 1, _mul);
            Token lb = new Token(@"\(", "LEFTBRACKET", 1, _lb);
            Token rb = new Token(@"\)", "RIGHTBRACKET", 1, _rb);
            Token num = new Token(@"[1-9][0-9]*", "NUMBER", Int32.MaxValue, _num);

            List<Token> TokenList = new List<Token> { id, num, add, mul, lb, rb };

            Lexer lexer = new Lexer(TokenList);

            Nonterminal Z = new Nonterminal(0, "Start");
            Nonterminal E = new Nonterminal(1, "E");
            Nonterminal T = new Nonterminal(2, "T");
            Nonterminal F = new Nonterminal(3, "F");

            Production P0 = new Production(0, Z, E);
            Production P1 = new Production(1, E, E, _add, T);
            Production P2 = new Production(2, E, T);
            Production P3 = new Production(3, T, T, _mul, F);
            Production P4 = new Production(4, T, F);
            Production P5 = new Production(5, F, _id);
            Production P6 = new Production(6, F, _num);
            Production P7 = new Production(6, F, _lb, E, _rb);

            Grammar grammar = new Grammar(Z, P0, P1, P2, P3, P4, P5, P6, P7);
            ParseTable parseTable = Builder.Build(grammar, Builder.BuildMethod.LR1, null);

            Parser parser = new Parser(parseTable, lexer, new myNodeFactory());
            return parser;
        }
    }

    public class myNodeFactory : INodeFactory
    {
        public INode OnNonterminal(Production production, Stack<INode> nodeStack)
        {
            if (production.ID == 1)
            {
                var mychilds = nodeStack.PopN(2).Cast<MathNode>().ToList();
                Console.WriteLine("create Nonterminal: {0} -> childs: {1}", "ADD", Tools.ListToString(mychilds));
                return new AddNode("addnode", mychilds[0], mychilds[1]);
            }
            if (production.ID == 3)
            {
                var mychilds = nodeStack.PopN(2).Cast<MathNode>().ToList();

                Console.WriteLine("create Nonterminal: {0} -> childs: {1}", "MUL", Tools.ListToString(mychilds));
                return new MulNode("mulnode", mychilds[0], mychilds[1]);
            }

            return null;
        }

        public INode OnTerminal(LexerPair lexerPair)
        {
            var terminal = lexerPair.Token.Terminal;
            Console.WriteLine("create Terminal: {0} -> ID: {1}", terminal.ToString(), terminal.ID);
            if (terminal.ID == 0)
                return new IDNode("idnode");

            if (terminal.ID == 5)
                return new NumNode("numnode", lexerPair.Lexeme);

            return null;
        }
    }



    public abstract class MathNode : INode
    {
        public abstract int Eval();

        string name;
        public MathNode(string s)
        {
            name = s;
        }
    }

    public class IDNode : MathNode
    {
        public IDNode(string s)
            : base(s)
        {
        }

        public override int Eval()
        {
            throw new NotImplementedException();
        }

        public override string ToString()
        {
            return "id";
        }
    }

    public class NumNode : MathNode
    {
        string lexeme;

        public NumNode(string s, string lex)
            : base(s)
        {
            lexeme = lex;
        }

        public override int Eval()
        {
            return Int32.Parse(lexeme);
        }

        public override string ToString()
        {
            return "num";
        }
    }

    public class MulNode : MathNode
    {
        MathNode left, right;

        public MulNode(string s, MathNode left, MathNode right)
            : base(s)
        {
            this.left = left;
            this.right = right;
        }

        public override int Eval()
        {
            return left.Eval() * right.Eval();
        }

        public override string ToString()
        {
            return "(" + left + "*" + right + ")";
        }
    }

    public class AddNode : MathNode
    {
        MathNode left, right;

        public AddNode(string s, MathNode left, MathNode right)
            : base(s)
        {
            this.left = left;
            this.right = right;
        }

        public override int Eval()
        {
            return left.Eval() + right.Eval();
        }

        public override string ToString()
        {
            return "(" + left + "+" + right + ")";
        }
    }
}
